package com.whosly.rapid.lang.thread.factory;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * InternalThreadFactory. <br>
 * 对应MizNamedThreadFactory  <br>
 *
 * <pre>
 * 使用方式: 构造方法或者create方法均可
 *
 * ScheduledExecutorService executorService  = Executors.newScheduledThreadPool(1, MizNamedThreadFactory
        .create("XXXX"));
 * </pre>
 *
 * @author yueny09 <deep_blue_yang@163.com>
 *
 * @DATE 2016年3月16日 上午3:35:48
 *
 */
public class NamedThreadFactory implements INamedThreadFactory {
	private static interface ClassifyStandard<T> {
		boolean satisfy(T thread);
	}

	private static final String DEFAULT_NAME = "default-pool";
	private static Logger log = LoggerFactory.getLogger(NamedThreadFactory.class);

	private static <T> void classify(final Set<T> src, final Set<T> des, final ClassifyStandard<T> standard) {
		final Set<T> set = new HashSet<>();
		for (final T t : src) {
			if (standard.satisfy(t)) {
				set.add(t);
			}
		}
		src.removeAll(set);
		des.addAll(set);
	}

	public static INamedThreadFactory create(final String namePrefix) {
		return create(namePrefix, false);
	}

	public static INamedThreadFactory create(final String namePrefix, final boolean daemon) {
		return new NamedThreadFactory(namePrefix, daemon);
	}

	public static ThreadGroup getThreadGroup() {
		SecurityManager s = System.getSecurityManager();
		ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
				.getThreadGroup();
		return group;
	}

	public static boolean waitAllShutdown(final int timeoutInMillis) {
		final ThreadGroup group = getThreadGroup();

		final Thread[] activeThreads = new Thread[group.activeCount()];
		group.enumerate(activeThreads);
		final Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
		final Set<Thread> dies = new HashSet<Thread>();
		log.info("Current ACTIVE thread count is: {}", alives.size());
		final long expire = System.currentTimeMillis() + timeoutInMillis;
		while (System.currentTimeMillis() < expire) {
			classify(alives, dies, new ClassifyStandard<Thread>() {
				@Override
				public boolean satisfy(final Thread thread) {
					return !thread.isAlive() || thread.isInterrupted() || thread.isDaemon();
				}
			});
			if (alives.size() > 0) {
				log.info("Alive threads: {}", alives);
				try {
					TimeUnit.SECONDS.sleep(2);
				} catch (final InterruptedException ex) {
					// ignore
				}
			} else {
				log.info("All threads are shutdown.");
				return true;
			}
		}
		log.warn("Some threads are still alive but expire time has reached, alive threads: {}", alives);
		return false;
	}

	@Getter
	private final boolean daemon;

	private final ThreadGroup threadGroup;
	@Getter
	private final String namePrefix;

	// @Getter
	// private final AtomicLong POOL_SEQ = new AtomicLong(1);
	private final AtomicLong threadNumber = new AtomicLong(1);

	private UncaughtExceptionHandler uncaughtExceptionHandler;

	@Deprecated
	public NamedThreadFactory() {
		this(DEFAULT_NAME, false);
	}

	public NamedThreadFactory(final String namePrefix) {
		this(namePrefix, false);
	}

	public NamedThreadFactory(final String namePrefix, final boolean daemon) {
		this(namePrefix, false, null);
	}

	public NamedThreadFactory(final String namePrefix, final boolean daemon,
			final UncaughtExceptionHandler uncaughtExceptionHandler) {
		this.namePrefix = namePrefix;
		this.daemon = daemon;

		 final SecurityManager s = System.getSecurityManager();
		 threadGroup = (s == null) ? Thread.currentThread().getThreadGroup() :
		 s.getThreadGroup();

		if (uncaughtExceptionHandler != null) {
			this.uncaughtExceptionHandler = uncaughtExceptionHandler;
		}
	}

	public NamedThreadFactory(final String prefix, final UncaughtExceptionHandler uncaughtExceptionHandler) {
		this(prefix, false, uncaughtExceptionHandler);
	}

	@Override
	public Thread newThread(final Runnable runnable) {
		// 此处子线程还未创建，故得到的是当前线程(也就是父线程的logid)
		// InheritableThreadHolder.holder.get()

		final Thread thread = new Thread(threadGroup, runnable, //
				namePrefix + "-" + threadNumber.getAndIncrement(), 0);

		if (uncaughtExceptionHandler != null) {
			thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
		}
		thread.setDaemon(daemon);

		if (thread.getPriority() != Thread.NORM_PRIORITY) {
			thread.setPriority(Thread.NORM_PRIORITY);
		}

		return thread;
	}

}